﻿using System;
using System.Globalization;
using Ewk.Math.Algebra;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Ewk.MachineLearning.Clustering.MultidimensionalScaling.UnitTests
{
    [TestClass]
    public class DistanceContainerTests : AlgebraUnitTestsBase<double>
    {
        [TestMethod, ExpectedException(typeof (ArgumentNullException))]
        public void DistanceContainer_constructor_throws_an_ArgumentNullException_when_the_argument_that_should_contain_an_array_of_Vectors_is_null()
        {
            new DistanceContainer<double>(null);
        }

        [TestMethod]
        public void DistanceContainer_constructor_takes_an_array_of_Vectors_and_calculates_distances_between_those_Vectors()
        {
            var vector1 = CreateVector(1, 2);
            var vector2 = CreateVector(2, 1);
            var vector3 = CreateVector(2, 2);

            var distances = new DistanceContainer<double>(vector1, vector2, vector3);

            Assert.AreEqual(System.Math.Sqrt(2.0), distances[0, 1].Value);
            Assert.AreEqual(distances[0, 1].Value, distances[1, 0].Value);

            Assert.AreEqual(1.0, distances[1, 2].Value);
            Assert.AreEqual(distances[1, 2].Value, distances[2, 1].Value);

            Assert.AreEqual(1.0, distances[0, 2].Value);
            Assert.AreEqual(distances[0, 2].Value, distances[2, 0].Value);
        }

        [TestMethod, ExpectedException(typeof (InvalidOperationException))]
        public void DistanceContainer_throws_an_InvalidOperationException_when_the_distance_from_or_to_a_not_existing_Vector_is_retrieved()
        {
            var vector1 = CreateVector(1, 2);

            var distances = new DistanceContainer<double>(vector1);
            var dist = distances[0, 2];
        }

        [TestMethod, ExpectedException(typeof (InvalidOperationException))]
        public void DistanceContainer_throws_an_InvalidOperationException_when_a_negative_index_is_used_to_retrieve_a_distance()
        {
            var vector1 = CreateVector(1, 2);

            var distances = new DistanceContainer<double>(vector1);
            var dist = distances[-1, 0];
        }

        [TestMethod]
        public void DistanceContainer_contains_the_correct_number_of_distances()
        {
            var matrix = CreateMatrix(231, 100, (i, j) => i + j);
            var distanceCount = matrix.RowCount*(matrix.RowCount - 1)/2;

            var vectors = new Vector<double>[matrix.RowCount];
            for (var i = 0; i < matrix.RowCount; i++)
            {
                vectors[i] = matrix.GetRow(i);
            }

            var distances = new DistanceContainer<double>(vectors);

            for (var i = 0; i < matrix.RowCount; i++)
            {
                for (var j = i + 1; j < matrix.RowCount; j++)
                {
                    Assert.AreEqual((j - i)*10, distances[i, j].Value);
                    Assert.AreEqual(distances[i, j].Value, distances[j, i].Value);
                    distanceCount--;
                }
            }

            Assert.AreEqual(0, distanceCount);
        }

        [TestMethod]
        public void DistanceContainer_VectorCount_returns_the_number_of_Vectors_contained_in_the_container()
        {
            var distances = new DistanceContainer<double>(
                CreateVector(1, 1),
                CreateVector(2, 2),
                CreateVector(3, 3),
                CreateVector(4, 4)
                );

            Assert.AreEqual(4, distances.VectorCount);
        }
    }
}